package com.chia.multienty.core.service.impl;

import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chia.multienty.core.domain.bo.workflow.ProcessDeployResult;
import com.chia.multienty.core.domain.dto.UploadedFileDTO;
import com.chia.multienty.core.domain.dto.WorkflowProcessDTO;
import com.chia.multienty.core.domain.enums.HttpResultEnum;
import com.chia.multienty.core.domain.enums.StatusEnum;
import com.chia.multienty.core.dubbo.service.DubboMultientyService;
import com.chia.multienty.core.dubbo.service.DubboWorkflowService;
import com.chia.multienty.core.exception.KutaRuntimeException;
import com.chia.multienty.core.mapper.WorkflowProcessMapper;
import com.chia.multienty.core.mybatis.MTLambdaWrapper;
import com.chia.multienty.core.mybatis.service.impl.KutaBaseServiceImpl;
import com.chia.multienty.core.parameter.workflow.*;
import com.chia.multienty.core.pojo.WorkflowProcess;
import com.chia.multienty.core.properties.yaml.YamlMultientyProperties;
import com.chia.multienty.core.service.WorkflowProcessService;
import com.chia.multienty.core.tools.IdWorkerProvider;
import com.chia.multienty.core.util.ListUtil;
import com.github.yulichang.toolkit.MPJWrappers;
import io.jsonwebtoken.lang.Strings;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.Paths;

/**
 * <p>
 * 流程 服务实现类
 * </p>
 *
 * @author Multienty Auto Generator
 * @since 2024-05-14
 */
@Service
@RequiredArgsConstructor
public class WorkflowProcessServiceImpl extends KutaBaseServiceImpl<WorkflowProcessMapper, WorkflowProcess>
        implements WorkflowProcessService {

    @Autowired(required = false)
    private DubboMultientyService dubboMultientyService;
    @Autowired(required = false)
    private DubboWorkflowService dubboWorkflowService;
    private final YamlMultientyProperties properties;

    @Override
    public WorkflowProcessDTO getDetail(WorkflowProcessDetailGetParameter parameter) {
        return selectJoinOne(WorkflowProcessDTO.class,
                        MPJWrappers.<WorkflowProcess>lambdaJoin()
                                .eq(parameter.getProcessId() != null,WorkflowProcess::getProcessId,
                                        parameter.getProcessId())
                                .eq(Strings.hasText(parameter.getKey()), WorkflowProcess::getDeployedKey, parameter.getKey())
                                .exists(Strings.hasText(parameter.getTypeAlias()),
                                        "SELECT 1 FROM mt_process_type WHERE alias={0} and `value` = t.type", parameter.getTypeAlias())
                                .eq(parameter.getTenantId() != null, WorkflowProcess::getTenantId, parameter.getTenantId())
                                .eq(parameter.getAppId()!=null, WorkflowProcess::getAppId, parameter.getAppId())
        );
    }

    @Override
    public void update(WorkflowProcessUpdateParameter parameter) {
        WorkflowProcess workflowProcess = new WorkflowProcess();
        BeanUtils.copyProperties(parameter, workflowProcess);
        updateByIdTE(workflowProcess);
    }

    @Override
    public void updateXml(WorkflowProcessXmlUpdateParameter parameter) {
        UploadedFileDTO uploadedFile = dubboMultientyService.getUploadedFile(parameter.getFileId());
        String prefix = properties.getFile().getLocalPrefixPath();

        File file = Paths.get(prefix, uploadedFile.getPath()).toFile();
        if(file.exists()) {
            FileUtil.writeString(parameter.getXml(), file, Charset.forName("utf-8"));
        } else{
            throw new KutaRuntimeException(HttpResultEnum.FILE_NOT_EXISTS);
        }
    }

    @Override
    @ShardingSphereTransactionType(TransactionType.BASE)
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void deploy(WorkflowProcessDeployParameter parameter) {
        WorkflowProcess process = getById(parameter.getProcessId());
        UploadedFileDTO file = dubboMultientyService.getUploadedFile(process.getFileId());
        FlowableDeployParameter deployParameter = new FlowableDeployParameter();
        deployParameter.setName(process.getName());
        deployParameter.setResourceName(process.getDeployedKey() + ".bpmn");
        deployParameter.setTenantId(process.getTenantId() != null ? process.getTenantId().toString() : null);
        deployParameter.setKey(process.getDeployedKey());
        deployParameter.setCategory(process.getCategory());
        String prefix = properties.getFile().getLocalPrefixPath();
        String path = Paths.get(prefix, file.getPath()).toString();
        deployParameter.setFilePath(path);
        ProcessDeployResult deploy = dubboWorkflowService.deploy(deployParameter);
        process.setDeployedId(deploy.getDeployId());
        process.setDeployedKey(deploy.getDeployKey());
        process.setStatus(StatusEnum.DEPLOYED.getCode());
        process.setProcessDefinitionId(deploy.getProcessDefinitionId());
        updateById(process);
    }

    @Override
    @ShardingSphereTransactionType(TransactionType.BASE)
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void redeploy(WorkflowProcessRedeployParameter parameter) {
        WorkflowProcess process = getById(parameter.getProcessId());
        UploadedFileDTO file = dubboMultientyService.getUploadedFile(process.getFileId());
        String prefix = properties.getFile().getLocalPrefixPath();
        String path = Paths.get(prefix, file.getPath()).toString();
        FlowableRedeployParameter redeployParameter = new FlowableRedeployParameter();
        redeployParameter.setDeployedId(process.getDeployedId());
        redeployParameter.setFilePath(path);
        ProcessDeployResult redeploy = dubboWorkflowService.redeploy(redeployParameter);
        process.setDeployedId(redeploy.getDeployId());
        process.setDeployedKey(redeploy.getDeployKey());
        process.setProcessDefinitionId(redeploy.getProcessDefinitionId());
        updateById(process);
    }

    @Override
    @ShardingSphereTransactionType(TransactionType.BASE)
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @SneakyThrows
    public void delete(WorkflowProcessDeleteParameter parameter) {
        WorkflowProcess process = getBy(parameter.getProcessId(),
                WorkflowProcess::getDeployedId,
                WorkflowProcess::getFileId);
        removeByIdTE(parameter.getProcessId());
        dubboWorkflowService.deleteDeploy(new FlowableDeployDeleteParameter().setDeployedId(process.getDeployedId()));
        dubboMultientyService.deleteFile(process.getFileId());
    }

    @Override
    public IPage<WorkflowProcessDTO> getPage(WorkflowProcessPageGetParameter parameter) {
        return selectJoinListPage(parameter.getPageObj(), WorkflowProcessDTO.class,
                new MTLambdaWrapper<WorkflowProcess>()
                        .solveGenericParameters(parameter)
                        .in(!ListUtil.isEmpty(parameter.getProcessIds()),
                                WorkflowProcess::getProcessId,
                                parameter.getProcessIds())
        );
    }

    @Override
    public void save(WorkflowProcessSaveParameter parameter) {
        WorkflowProcess workflowProcess = new WorkflowProcess();
        BeanUtils.copyProperties(parameter, workflowProcess);
        workflowProcess.setProcessId(IdWorkerProvider.next());

        saveTE(workflowProcess);
        parameter.setProcessId(workflowProcess.getProcessId());
    }


    @Override
    public void enable(WorkflowProcessEnableParameter parameter) {
        WorkflowProcess workflowProcess = new WorkflowProcess();
        BeanUtils.copyProperties(parameter, workflowProcess);
        workflowProcess.setStatus(StatusEnum.NORMAL.getCode());
        updateByIdTE(workflowProcess);
    }

    @Override
    public void disable(WorkflowProcessDisableParameter parameter) {
        WorkflowProcess workflowProcess = new WorkflowProcess();
        BeanUtils.copyProperties(parameter, workflowProcess);
        workflowProcess.setStatus(StatusEnum.DISABLED.getCode());
        updateByIdTE(workflowProcess);
    }
}
